# Copyright 2018 Maco
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from urllib.parse import urlparse
from tornado import gen
from blockchain.handler.base import FrontendHandler, route
from blockchain.core.blockchain import Bchain
from blockchain.core.nodes import Nodes
from blockchain.common import constants
from blockchain.common.util import printf, request
from blockchain.core.broadcast import bc_register_node


@route(r'/node/vaild')
class NodeVaildHandler(FrontendHandler):

    def get(self):
        self.success(data={'name': constants.NODE_UNIQUE})


@route(r'/node/list')
class NodeListHandler(FrontendHandler):
    '''
    查询所有节点信息
    :url: /node/list
    :methods: GET
    '''

    def get(self):
        self.success(data={'nodes': list(Nodes.nodes)})


@route(r'/node/rc/sync')
class RcNodeHandler(FrontendHandler):
    '''
    :url: /node/rc/sync
    :methods: POST
    :param: json e.g:{"nodes":["127.0.0.1:8000"],"_nodes":["127.0.0.1:8000"]}
            nodes注册的节点列表，_nodes广播的节点列表
    '''
    @gen.coroutine
    def post(self):
        data = self.get_request_body()
        if not data.get('nodes', None):
            self.failure('The node list is empty.')
        else:
            for node in data['nodes']:
                Nodes.register(f'http://{node}')
            if data.get('_nodes', None):
                self.bc_other_nodes(data['_nodes'])
            self.success('Sync nodes success.')

    @gen.coroutine
    def bc_other_nodes(self, sync_nodes):
        yield bc_register_node(sync_nodes, list(Nodes.nodes))


@route(r'/node/register')
class RegisterNodeHandler(FrontendHandler):
    '''
    :url: /node/register
    :methods: POST
    :param: json serializable, e.g: {"nodes":["http://127.0.0.1:8000"]}
    '''
    @gen.coroutine
    def post(self):
        nodes = self.get_request_body()
        if nodes.get('nodes') is None:
            self.failure('Please supply a valid list of nodes.')
        else:
            for node in nodes['nodes']:
                try:
                    url = urlparse(node).netloc
                    if not url:
                        raise ValueError('Illegal url.')
                    resp = yield request(f'http://{url}/api/node/vaild')
                except ValueError:
                    printf(f'Illegal url \'{node}\'.')
                    resp = None
                if not resp or resp.code != constants.CODE_OK:
                    continue
                Nodes.register(node)
            self.bc_all_nodes()
            data = {'total': len(Nodes.nodes)}
            self.success('Register nodes success.', data=data)

    @gen.coroutine
    def bc_all_nodes(self):
        yield bc_register_node(list(Nodes.nodes), list(Nodes.nodes))


@route(r'/node/resolve')
class ResolveNodeHandler(FrontendHandler):
    '''
    解决冲突，使用网络中最长的链
    :url: /node/resolve
    :methods: GET
    '''

    @gen.coroutine
    def get(self):
        result = yield Nodes.resolve_conflicts()
        if result:
            message = 'Our chain was replaced.'
        else:
            message = 'Our chain is authoritative.'
        self.success(message, data=Bchain.chain)
